﻿using System.Diagnostics;
using System.Text;
using DimensionsHelper.LanguageService.Protocol;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

public struct LexerState(
    DocumentUri uri,
    string text,
    int position,
    SyntaxList<SyntaxComment> comments)
{
    private TextPointer _pointer = new(text, position);

    // 当前的 NodeFlag，和_lastToken和_currentToken中的不一定相同
    private SyntaxNodeFlags _flags = SyntaxNodeFlags.StartOfFile;
    private LexerTokenInfo _currentToken;

    private int _skippedEmptyLines;

    // _commentPos 和 _commentStartPos 在添加注释时会递增，因此起始是-1
    private int _commentPos = -1;
    private int _commentStartPos = -1;
    private bool _startLexingComment = false;

    public SourceNode StepToken(SyntaxKinds kind, int step = 1, string? tokenText = null,
        SyntaxNodeFlags flags = SyntaxNodeFlags.None)
    {
        _pointer.Step(step);
        FinishToken();
        _currentToken.Flags = _flags | flags;

        // 在没有换行时，保留 PreprocessorIfCondition
        if ((_flags & SyntaxNodeFlags.PreprocessorIfCondition) != 0 &&
            (_flags & SyntaxNodeFlags.PrecedingLinebreak) == 0)
        {
            _flags &= ~SyntaxNodeFlags.PreprocessorIfCondition;
        }
        else
        {
            _flags = SyntaxNodeFlags.None;
        }

        if (tokenText != null)
        {
            _currentToken.Text = tokenText;
        }

        SourceNode node = NodeFactory.CreateSourceToken(kind);
        node.SetFlags(_currentToken.Flags);
        node.Update(_currentToken.Start, _currentToken.Length, _currentToken.Text);
        node.UpdateLine(_currentToken.StartLine, _currentToken.EndLine);
        return node;
    }


    /// <summary>
    ///     如果文本指针在文本开头，返回<see langword="true" />
    /// </summary>
    public readonly bool AtStartOfFile => (_flags & SyntaxNodeFlags.StartOfFile) != 0;


    /// <summary>
    ///     跳过指定数量的字符
    /// </summary>
    public void Step(uint charNumber)
    {
        _pointer.Step((int)charNumber);
    }


    /// <summary>
    ///     获取当前的文本指针位置
    /// </summary>
    public readonly int GetPosition()
    {
        return _pointer.GetTextPosition();
    }


    /// <summary>
    ///     获取当前位置的字符
    /// </summary>
    public readonly char GetCurrentChar()
    {
        return _pointer.Current;
    }


    /// <summary>
    ///     获取当前位置的行号，1开始。
    /// </summary>
    private readonly int GetLine()
    {
        return _pointer.Line;
    }


    /// <summary>
    ///     使用当前文本位置更新Token的起始位置
    /// </summary>
    public void StartToken()
    {
        _currentToken.Start = GetPosition();
        _currentToken.StartLine = GetLine();
        _currentToken.EndLine = GetLine();
        _currentToken.Length = 0;
        _currentToken.Text = string.Empty;
    }


    /// <summary>
    ///     获取Token的起始位置
    /// </summary>
    public readonly int GetTokenStart()
    {
        return _currentToken.Start;
    }


    /// <summary>
    ///     使用当前位置更新Token的结束位置
    /// </summary>
    private void FinishToken()
    {
        _currentToken.Length = GetPosition() - GetTokenStart();
        _currentToken.EndLine = GetLine();
    }


    /// <summary>
    ///     获取Token的结束位置
    /// </summary>
    public readonly int GetTokenEnd()
    {
        return _currentToken.End;
    }


    /// <summary>
    ///     如果当前的<see cref="SyntaxNodeFlags" />包含<paramref name="flags" />
    ///     的任意一个二进制标记，返回<see langword="true" />
    /// </summary>
    public readonly bool GetFlags(SyntaxNodeFlags flags)
    {
        return _flags.HasFlag(flags);
    }


    /// <summary>
    ///     获取当前的<see cref="SyntaxNodeFlags" />值
    /// </summary>
    public readonly SyntaxNodeFlags GetFlags()
    {
        return _flags;
    }


    /// <summary>
    ///     添加一个新的<see cref="SyntaxNodeFlags" />
    /// </summary>
    public void SetFlags(SyntaxNodeFlags flags)
    {
        _flags |= flags;
    }


    /// <summary>
    ///     移除指定的<see cref="SyntaxNodeFlags" />
    /// </summary>
    public void RemoveFlags(SyntaxNodeFlags flags)
    {
        _flags &= ~flags;
    }


    /// <summary>
    ///     清空当前的<see cref="SyntaxNodeFlags" />
    /// </summary>
    public void ClearFlags(SyntaxNodeFlags keep = SyntaxNodeFlags.None)
    {
        _flags &= keep;
    }


    /// <summary>
    ///     截取字符串
    /// </summary>
    public readonly string Substring(int start, int length)
    {
        Debug.Assert(start >= 0 && length >= 0 && length + start <= text.Length);
        return text.Substring(start, length);
    }


    /// <summary>
    ///     如果已经到文本结尾，返回<see langword="true" />
    /// </summary>
    public readonly bool Eof()
    {
        return _pointer.Eof();
    }


    /// <summary>
    ///     读取当前行的文本内容直到换行符或末尾
    /// </summary>
    /// <param name="includeCurrentChar">是否包含当前字符</param>
    public string ReadToLineFeed(bool includeCurrentChar = true)
    {
        StringBuilder sb = new();
        SkipWhitespace(false);
        char ch = GetCurrentChar();
        bool isFirstChar = true;
        while (!Eof() && !IsLineBreak(ch))
        {
            if (isFirstChar)
            {
                if (includeCurrentChar)
                {
                    sb.Append(ch);
                }

                isFirstChar = false;
            }
            else
            {
                sb.Append(ch);
            }

            ch = _pointer.Next();
        }

        return sb.ToString();
    }


    private bool TrySkipLineFeed(bool updatePointer)
    {
        if (Eof())
        {
            return false;
        }

        TextPointer pointer = _pointer; // 复制一个文本指针
        char c = pointer.Next(); // 当前的下一个字符

        switch (c)
        {
            case '\n':
            {
                pointer.Step(1);
                if (updatePointer)
                {
                    _pointer = pointer;
                }

                return true;
            }
            case '\r' when pointer.Ahead() == '\n':
            {
                pointer.Step(2);
                if (updatePointer)
                {
                    _pointer = pointer;
                }

                return true;
            }
            default:
                return false;
        }
    }


    private char SkipLineFeed()
    {
        while (!Eof())
        {
            char ch = _pointer.Current;
            // _ 或 \
            if ((ch != '_' && ch != '\\') || !TrySkipLineFeed(true))
            {
                break;
            }
        }

        return _pointer.Current;
    }


    private static bool IsLineBreak(char c)
    {
        return c == '\r'
               || c == '\n'
               || c == 8232 // Line separator
               || c == 8233 // Paragraph separator
            ;
    }


    private static bool IsWhiteSpace(char c)
    {
        return c == ' '
               || c == '\t'
               || c == 5760
            ;
    }


    /// <summary>
    ///     如果当前位置包含空白，跳过所有空白字符
    /// </summary>
    private void SkipWhitespace(bool skipLinefeed = true)
    {
        _skippedEmptyLines = 0;
        char ch = _pointer.Current;
        while (IsWhiteSpace(ch) || (skipLinefeed && IsLineBreak(ch)))
        {
            if (ch == '\n')
            {
                _flags |= SyntaxNodeFlags.PrecedingLinebreak;
                ++_skippedEmptyLines;
            }

            ch = _pointer.Next();
        }
    }


    private void SkipChars(char ch)
    {
        char c = _pointer.Current;
        while (c == ch)
        {
            c = _pointer.Next();
        }
    }


    // ' 开始，到行末结束
    private void SkipLineComment(bool isCComment)
    {
        int start = GetPosition();
        // C风格注释：//
        SkipChars(isCComment ? '/' : '\'');
        // 跳过空格
        SkipWhitespace(false);

        string line = ReadToLineFeed();

        // 如果 _commentIndex 前进一步后小于注释列表宽度，可以跳过
        if (_commentPos + 1 < comments.Count)
        {
            ++_commentPos;
            return;
        }

        SyntaxComment comment = NodeFactory.CreateLineComment(start, GetPosition() - start, GetLine(), line);
        comment.UpdateLine(GetLine(), GetLine());
        comment.IsCStyleComment = isCComment;
        comment.SetUri(uri);
        AddComment(comment);
    }


    // '! 开始， !' 结束。
    private void SkipBlockComment(bool isCComment)
    {
        int start = GetPosition();
        // 跳过 '!
        Step(2);
        int end = start;

        TrySkipLineFeed(true);

        List<(int Start, int Length, int StartLine, string Line)> lines = [];
        StringBuilder str = new();
        int currentStart = GetPosition();

        while (!Eof())
        {
            char c = GetCurrentChar();
            // !'
            bool ending = isCComment
                ? c == '*' && LookAhead() == '/'
                : c == '!' && LookAhead() == '\'';
            // \r\n, \n, !'
            if (c == '\r' || c == '\n' || ending)
            {
                lines.Add((currentStart,
                    GetPosition() - currentStart,
                    GetLine(),
                    str.ToString()));

                str.Clear();

                if (c == '\r' && LookAhead() == '\n')
                {
                    Step(1);
                    SkipChars(isCComment ? '*' : '\'');
                    SkipWhitespace(false);
                    currentStart = GetPosition();
                }
            }
            else
            {
                str.Append(c);
            }

            // end
            if (ending)
            {
                Step(2);
                end = GetPosition();
                break;
            }

            Step(1);
        }

        // 如果 _commentIndex 前进一步后小于注释列表宽度，可以跳过
        if (_commentPos + 1 < comments.Count)
        {
            ++_commentPos;
            return;
        }

        SyntaxComment comment = NodeFactory.CreateBlockComment([.. lines]);
        comment.IsCStyleComment = isCComment;
        comment.Update(start, end, Substring(start, end - start));
        comment.SetUri(uri);
        AddComment(comment);
    }


    /// <summary>
    ///     跳过当前位置后的空白和注释。
    /// </summary>
    public void SkipTrivia()
    {
        _commentStartPos = _commentPos;
        _startLexingComment = true;

        while (!Eof())
        {
            SkipWhitespace();
            char ch = GetCurrentChar();

            if (ch == '\'')
            {
                // 不考虑换行符
                if (LookAhead() == '!')
                {
                    SkipBlockComment(false);
                }
                else
                {
                    SkipLineComment(false);
                }
            }
            else if (GetFlags(SyntaxNodeFlags.PreprocessorIfCondition) && ch == '/')
            {
                // 不考虑换行符
                switch (LookAhead())
                {
                    case '/':
                        SkipLineComment(true);
                        break;
                    case '*':
                        SkipBlockComment(true);
                        break;
                }
            }
            else
            {
                break;
            }

            _startLexingComment = false;
        }
    }


    /// <summary>
    ///     跳转到下一个非空白字符，如果到文件末尾，返回'\0'
    /// </summary>
    /// <param name="skipLinefeed">是否跳过换行符</param>
    public char NextNotWhitespace(bool skipLinefeed = false)
    {
        char ch = _pointer.Next();

        if (Eof())
        {
            return '\0';
        }

        while (IsWhiteSpace(ch) || (skipLinefeed && IsLineBreak(ch)))
        {
            ch = _pointer.Next();
        }

        return _pointer.Current;
    }


    public char SkipWrappedLine()
    {
        char ch = _pointer.Current;

        if (ch is not ('\\' or '_') || _pointer.Ahead() is not ('\r' or '\n'))
        {
            return ch;
        }

        SkipLineFeed();
        SkipWhitespace(false);
        return _pointer.Current;
    }


    /// <summary>
    ///     获取下一个字符，此方法会跳过脚本换行符'\'和'_'
    /// </summary>
    public char Next()
    {
        _pointer.Next();
        return Eof() ? '\0' : SkipWrappedLine();
    }


    /// <summary>
    ///     获取下一个字符
    /// </summary>
    public readonly char LookAhead()
    {
        return _pointer.Ahead();
    }


    /// <summary>
    ///     如果下一个字符是指定字符，返回<see langword="true" />
    /// </summary>
    public readonly bool LookAheadIs(char ch)
    {
        return _pointer.Ahead() == ch;
    }


    /// <summary>
    ///     如果下一个字符是给定列表中的一个，返回<see langword="true" />
    /// </summary>
    public readonly bool LookAheadIsOneOf(params char[] chs)
    {
        return Array.IndexOf(chs, LookAhead()) != -1;
    }


    /// <summary>
    ///     获取当前的连续注释列表
    /// </summary>
    public readonly Span<SyntaxComment> GetComments()
    {
        return comments.AsSpan();
    }


    /// <summary>
    ///     获取当前位置的最新的注释
    /// </summary>
    public readonly SyntaxComment? GetCurrentComment()
    {
        return _commentPos == -1 || _commentPos >= comments.Count || _commentStartPos >= _commentPos
            ? null
            : comments[_commentPos];
    }


    /// <summary>
    ///     获取当前节点区间内的注释列表。
    /// </summary>
    public readonly Span<SyntaxComment> GetCurrentComments()
    {
        if (_commentPos < 0 || _commentStartPos >= _commentPos)
        {
            return Span<SyntaxComment>.Empty;
        }

        return comments.AsSpan().Slice(_commentStartPos + 1, _commentPos - _commentStartPos);
    }


    /// <summary>
    ///     如果当前注释应当附在当前<see cref="SyntaxNode" />上，返回<see langword="true" />。
    /// </summary>
    public readonly bool IsCurrentCommentAttached =>
        _commentPos >= 0 && _skippedEmptyLines <= 1;


    /// <summary>
    ///     向列表中添加一个注释节点
    /// </summary>
    private void AddComment(SyntaxComment comment)
    {
        // 读取注释时，换行符在此方法后跳过，此时跳过的空行数是当前注释前的空行数。
        comment.LeadingEmptyLines = _skippedEmptyLines > 0 ? _skippedEmptyLines - 1 : 0;

        // 如果添加的是行注释，检查 _skippedEmptyLines，如果等于1，
        // 将行注释追加到上一行注释中。
        //
        // ' Line1  -+
        // ' Line2   |--- 这三行放在一个行注释中
        // ' Line3  -+
        //
        // ' Line4  ----- 因为中间有空行，单独在一个行注释中
        //
        if (!_startLexingComment && /* 开启新的注释时，不检查行注释是否连续 */
            _skippedEmptyLines <= 1 && comment.IsLineComment &&
            !comments.IsEmpty && comments.Last().IsLineComment)
        {
            comments.Last().AddLines(comment);
            return;
        }

        comments.Add(comment);
        ++_commentPos;
    }
}